"""随机森林模型"""
import datetime
import pickle

import warnings
warnings.filterwarnings('ignore')

import joblib
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics import f1_score, precision_score, recall_score, accuracy_score
from sklearn.model_selection import train_test_split
from tqdm import tqdm

from src.Commons.data_util import cat_sentence

pd.set_option('display.expand_frame_repr', False)  # 避免宽表格换行
pd.set_option('display.max_columns', None)  # 确保所有列可见

from src.Commons.base_config_loader import BaseConfigLoader


def get_tfidf_obj(config, stop_words_path):
    """
    TF-IDF（词频-逆向文件频率）是一种将文本转化为数值向量的向量化方法
    """
    stop_words_path = config.get(stop_words_path)
    with open(stop_words_path, 'r', encoding='utf-8') as f:
        stop_words = f.read().splitlines()
    tfidf = TfidfVectorizer(stop_words=stop_words)

    return tfidf


def load_data(config, data_path, tfidf: TfidfVectorizer):
    data_path = config.get(data_path)
    df = pd.read_csv(filepath_or_buffer=data_path, sep=config.csv_sep,encoding="utf-8", nrows=20000, low_memory=False)

    labels = df['label']
    features = tfidf.fit_transform(df['word'])
    print("features shape:",features.shape)
    # print("特征名称列表:", list(tfidf.get_feature_names_out()))
    # print("特征数量:", len(tfidf.get_feature_names_out()))
    # print("特征索引:", tfidf.vocabulary_)
    # print("特征索引长度:", len(tfidf.vocabulary_))

    del df
    return features, labels


def model_train(config, features, labels, model_rf_path, model_tfidf_vocab_path):
    model_rf_path = config.get(model_rf_path)
    model_tfidf_vocab_path = config.get(model_tfidf_vocab_path)

    # x_train：训练集的特征数据（占总数据的 80%），用于模型训练。
    # x_test：测试集的特征数据（占总数据的 20%），用于评估模型性能。
    # y_train：训练集的标签（与 x_train 一一对应），用于训练时的监督学习。
    # y_test：测试集的标签（与 x_test 一一对应），用于评估模型预测的准确性。
    x_train, x_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)
    rf = RandomForestClassifier()
    start_time = datetime.datetime.now()
    print(f"基线模型随机森林开始训练，训练集特征形状:{x_train.shape}，训练集标签形状:{y_train.shape}，时间：{start_time}")
    for epoch in tqdm(range(1,2)):
        rf.fit(x_train, y_train)

    print(f"基线模型随机森林开始预测和评估，测试集特征形状:{x_test.shape}，时间：{datetime.datetime.now()}")
    y_pred = rf.predict(x_test)
    print(f"基线模型随机森林，测试集准确率：{accuracy_score(y_test, y_pred)}，测试集F1值：{f1_score(y_test, y_pred, average='weighted')}")
    print(f"基线模型随机森林，精确率 (macro):{precision_score(y_test, y_pred, average='macro')},召回率 (macro):{recall_score(y_test, y_pred, average='macro')}")
    print(f"基线模型随机森林训练完成，时间：{datetime.datetime.now()}")

    # 保存模型和向量化器
    # joblib.dump(rf, model_rf_path)
    # joblib.dump(tfidf, model_tfidf_path)
    # 在不同 Python 环境间的兼容性更好，推荐使用 pickle
    with open(model_rf_path, 'wb') as f:
        pickle.dump(rf, f)
    with open(model_tfidf_vocab_path, 'wb') as f:
        pickle.dump(tfidf, f)
    print(f"基线模型随机森林模型:{model_rf_path},和TF-IDF向量化器:{model_tfidf_vocab_path},保存完成，时间：{datetime.datetime.now()}")

def model_predict(config, dev_process_path, model_rf_path, model_tfidf_vocab_path, model_result_path):
    dev_process_path = config.get(dev_process_path)
    model_rf_path = config.get(model_rf_path)
    model_tfidf_vocab_path = config.get(model_tfidf_vocab_path)
    model_result_path = config.get(model_result_path)

    with open(model_rf_path, 'rb') as f:
        rf = pickle.load(f)
    with open(model_tfidf_vocab_path, 'rb') as f:
        tfidf = pickle.load(f)

    df = pd.read_csv(dev_process_path, sep=config.csv_sep)
    features = tfidf.transform(df['word'])
    pred = rf.predict(features)
    print(f"特征形状Features:{features.shape}")
    # 准确率，是预测的正确样本，占总样本的比例
    print(f"准确率：{accuracy_score(df['label'], pred)}")
    # 精确率，是预测为正的样本中，实际为正的比例
    # 宏（macro）F1值,平等对待每个类别，适合关注少数类表现，
    # 微观平均(micro)，受多数类影响更大，适合关注整体效果
    print(f"精确率 (macro):{precision_score(df['label'], pred, average='micro')}")
    # 召回率，是实际为正的样本中，预测为正的比例
    print(f"召回率 (macro):{recall_score(df['label'], pred, average='macro')}")
    # F1值，是精确率和召回率的调和平均值
    print(f"F1值 (macro):{f1_score(df['label'], pred, average='macro')}")

    df["pred"] = pred
    df.to_csv(model_result_path, index=False)
    del df

def rf_predict(context: str):
    config_loader = BaseConfigLoader()
    model_rf_path = "system.resources.random_forest.models.model_rf_path"
    model_tfidf_vocab_path = "system.resources.random_forest.models.model_tfidf_vocab_path"
    class_path = "system.resources.random_forest.class_path"

    return _rf_predict(context, config_loader, model_rf_path, model_tfidf_vocab_path, class_path)

def _rf_predict(context: str, config: BaseConfigLoader, model_rf_path: str, model_tfidf_vocab_path: str, class_path: str):
    model_rf_path = config.get(model_rf_path)
    model_tfidf_vocab_path = config.get(model_tfidf_vocab_path)
    class_path = config.get(class_path)

    with open(model_rf_path, 'rb') as f:
        rf = pickle.load(f)
    with open(model_tfidf_vocab_path, 'rb') as f:
        tfidf = pickle.load(f)
    with open(class_path, 'r', encoding='utf-8') as f:
        class_dict = f.read().splitlines()

    context = cat_sentence(context)
    features = tfidf.transform([context])
    # print(f"特征形状Features:{features.shape}")
    pred_idx = rf.predict(features)[0]
    print(f"预测结果的索引位置:{pred_idx}, 预测结果的类别:{class_dict[pred_idx]}")

    return class_dict[pred_idx]


if __name__ == '__main__':

    config_loader = BaseConfigLoader()

    train_process_path = "system.resources.random_forest.process_data.train_path"
    test_process_path = "system.resources.random_forest.process_data.test_path"
    dev_process_path = "system.resources.random_forest.process_data.dev_path"

    class_path = "system.resources.random_forest.class_path"
    stop_words_path = "system.resources.random_forest.stop_words_path"

    model_rf_path = "system.resources.random_forest.models.model_rf_path"
    model_tfidf_vocab_path = "system.resources.random_forest.models.model_tfidf_vocab_path"
    model_result_path = "system.resources.random_forest.models.model_result_path"

    tfidf = get_tfidf_obj(config_loader, stop_words_path)
    # print(tfidf.fit_transform(['含了文本中所有出现的词语']))
    features, labels = load_data(config_loader, train_process_path, tfidf)

    # model_train(config_loader, features, labels, model_rf_path, model_tfidf_vocab_path)

    # model_predict(config_loader, dev_process_path, model_rf_path, model_tfidf_vocab_path, model_result_path)

    rf_predict("这是一个测试句子")
